﻿using System;
using System.Collections.Generic;
using System.Linq;
using HK.Core.Common.Data;
using HK.Core.Common.Protocol;
using HK.Core.Common.Protocol.Data;
using HK.Core.IO.Protocol.Data;
using HK.Core.Utils;
using UnityEngine;

namespace HK.Core.IO.Data
{
    /// <summary>
    /// Jab文件中包含的子文件
    /// </summary>
    [Serializable]
    public sealed class JabChildFile : JsonData<JabChildFile>, IJabChildFile, IEquatable<JabChildFile>
    {

#region Create

        /// <summary>
        /// 创建Jab文件Item
        /// </summary>
        /// <param name="iJabName">Jab名(相对路径)</param>
        /// <returns>Jab文件Item</returns>
        public static JabChildFile Create(string iJabName)
        {
            return string.IsNullOrEmpty(iJabName) ? null : new JabChildFile { jabName = iJabName };
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private JabChildFile() {}
        
        [SerializeField]
        private string jabName = null;
        /// <summary>
        /// Jab名
        /// <para>* 相对目录。如：Assets/xxxx/yyyy.jab</para>
        /// </summary>
        public string JabName
        {
            get => jabName;
            private set => jabName = value;
        }

        [SerializeField]
        private string path = null;
        /// <summary>
        /// 路径
        /// <para>* 除去JabName以外的路径</para>
        /// </summary>
        public string Path
        {
            get => path;
            set => path = value;
        }
        
        [SerializeField]
        private int size = 0;
        /// <summary>
        /// 大小(非压缩)
        /// <para>* 单位 : Byte</para>
        /// </summary>
        public int Size
        {
            get => size;
            set => size = value;
        }

        [SerializeField]
        private int uncSize = 0;
        /// <summary>
        /// 压缩大小
        /// <para>* 单位 : Byte</para>
        /// </summary>
        public int UncSize 
        {
            get => uncSize;
            set => uncSize = value;
        }

        [SerializeField]
        private double time = 0.0d;
        public double Time 
        {
            get => time;
            set => time = value;
        }

        [SerializeField]
        private uint crc = 0;
        /// <summary>
        /// 验证码 - Crc
        /// </summary>
        public uint Crc 
        {
            get => crc;
            set => crc = value;
        }
        
        /// <summary>
        /// Crc有效标志位
        /// </summary>
        public bool CrcValid => 0 < crc;
        
        [SerializeField]
        private uint dataLocalOffset = 0;
        /// <summary>
        /// 有效数据在数据段中的偏移
        /// </summary>
        public uint DataLocalOffset 
        {
            get => dataLocalOffset;
            set => dataLocalOffset = value;
        }

        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(path);
        
#region Equals

        /// <summary>
        /// 比较
        /// </summary>
        /// <param name="iJabFile">Jab文件设定</param>
        /// <returns>true:相同; false:不相同;</returns>
        public bool Equals(JabChildFile iJabFile)
        {
            if (null == iJabFile || !iJabFile.Valid) return false;

            if (UtilsString.Diff(jabName, iJabFile.JabName)) return false;
            if (UtilsString.Diff(path, iJabFile.Path)) return false;
            if (size != iJabFile.Size) return false;
            if (uncSize != iJabFile.UncSize) return false;
            if (Math.Abs(time - iJabFile.Time) > Mathf.Epsilon) return false;
            if (crc != iJabFile.Crc) return false;
            if (dataLocalOffset != iJabFile.DataLocalOffset) return false;
            return true;
        }

#endregion

        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IJabChildFile info)
            {
                jabName = UpdateValue(jabName, info.JabName, iImport);
                path = UpdateValue(path, info.Path, iImport);
                size = UpdateValue(size, info.Size, iImport);
                uncSize = UpdateValue(uncSize, info.UncSize, iImport);
                time = UpdateValue(time, info.Time, iImport);
                crc = UpdateValue(crc, info.Crc, iImport);
                dataLocalOffset = UpdateValue(dataLocalOffset, info.DataLocalOffset, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            
            jabName = null;
            path = null;
            size = 0;
            uncSize = 0;
            time = 0.0d;
            crc = 0;
            dataLocalOffset = 0;
        }
    }

    /// <summary>
    /// Jab文件头部
    /// </summary>
    [Serializable]
    public sealed class JabHeader : JsonData<JabHeader>, IJabHeader<JabChildFile>
    {

#region Create

        /// <summary>
        /// 创建Jab文件头部
        /// </summary>
        /// <param name="iVersion">Version</param>
        /// <param name="iCompress">压缩标志位(1:zlib压缩)</param>
        /// <param name="iDataSectionGOffset">数据段位置</param>
        /// <param name="iChildCount">子文件数量</param>
        /// <returns>Jab文件头部</returns>
        public static JabHeader Create(byte iVersion, bool iCompress, 
            uint iDataSectionGOffset, uint iChildCount)
        {
            return new JabHeader
            {
                version = iVersion, compress = iCompress, dataSectionGOffset = iDataSectionGOffset, 
                childCount = iChildCount
            };
        }

#endregion

        /// <summary>
        /// 保护构造函数
        /// </summary>
        private JabHeader() {}
        
        [SerializeField]
        private byte version = 0;
        /// <summary>
        /// 版本
        /// <para>大于2 : 包含压缩格式设定</para>
        /// <para>大于3 : 包含有所在文件加前缀信息</para>
        /// </summary>
        public byte Version 
        {
            get => version;
            private set => version = value;
        }
        
        [SerializeField]
        private bool compress = false;
        /// <summary>
        /// 压缩标志位
        /// <para>* true : zlib压缩</para>
        /// </summary>
        public bool Compress
        {
            get => compress;
            private set => compress = value;
        }
        
        [SerializeField]
        private uint dataSectionGOffset = 0;
        /// <summary>
        /// 数据段位置
        /// </summary>
        public uint DataSectionGOffset 
        {
            get => dataSectionGOffset;
            private set => dataSectionGOffset = value;
        }
        
        [SerializeField] 
        private uint childCount = 0;
        /// <summary>
        /// 子文件个数
        /// </summary>
        public uint ChildCount 
        {
            get => childCount;
            private set => childCount = value;
        }
        
        [SerializeField] 
        private string prefix = null;
        /// <summary>
        /// 所在文件夹前缀
        /// </summary>
        public string Prefix 
        {
            get => prefix;
            set => prefix = value;
        }

        [SerializeField]
        private List<JabChildFile> children = new List<JabChildFile>();
        /// <summary>
        /// 子文件加列表
        /// </summary>
        public List<JabChildFile> Children => children;

        /// <summary>
        /// 追加子文件信息
        /// </summary>
        /// <param name="iChild">子节点</param>
        /// <returns>true:OK; false:NG;</returns>
        public bool AddChild(JabChildFile iChild)
        {
            if (null == iChild || !iChild.Valid) return false;

            if (children.Exists(iO => iO.Equals(iChild))) return false;
            
            children.Add(iChild);
            return true;
        }
        
        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IJabHeader<JabChildFile> header)
            {
                version = UpdateValue(version, header.Version, iImport);
                compress = UpdateValue(compress, header.Compress, iImport);
                dataSectionGOffset = UpdateValue(dataSectionGOffset, header.DataSectionGOffset, iImport);
                childCount = UpdateValue(childCount, header.ChildCount, iImport);
                prefix = UpdateValue(prefix, header.Prefix, iImport);

                dirty = UtilsArray.Diff<JabChildFile>(children, header.Children);
                if (dirty)
                {
                    children.Clear();
                    children.AddRange(header.Children);

                    if (!iImport)
                    {
                        Dirty = true;
                    }
                }
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            
            version = 0;
            compress = false;
            dataSectionGOffset = 0;
            childCount = 0;
            prefix = null;
            children.Clear();
        }
    }
}
